package cn.edu.sdjzu.xg23.kcsj01_backend.utils;

import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneOffset;

/**
 * 单节点雪花ID生成器
 * 固定工作节点ID和数据中心ID为0，生成64位长整型ID
 */
@Component
public class SnowflakeIdGenerator {

    // ---------------------- 固定参数（单节点场景） ----------------------
    private static final int WORKER_ID = 0;         // 工作节点ID（0~31，固定为0）
    private static final int DATA_CENTER_ID = 0;    // 数据中心ID（0~31，固定为0）
    private static final long SEQUENCE_MASK = 0xFFF; // 序列号掩码（0~4095，12位）
    private static final long START_TIMESTAMP = LocalDateTime.of(
            2025, 5, 19, 0, 0, 0 // 起始时间（自定义为课程设计开始时间）
    ).toInstant(ZoneOffset.UTC).toEpochMilli();

    // ---------------------- 内部状态 ----------------------
    private long sequence = 0L;        // 序列号（同一毫秒内自增）
    private long lastTimestamp = -1L;  // 上次生成ID的时间戳（毫秒）

    // ---------------------- 核心生成方法 ----------------------
    public synchronized long generateId() {
        long currentTimestamp = System.currentTimeMillis();

        // 1. 处理时钟回退（单节点场景几乎不会出现，但保留健壮性）
        if (currentTimestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards. Timestamp must be incrementing.");
        }

        // 2. 同一毫秒内，序列号自增
        if (currentTimestamp == lastTimestamp) {
            sequence = (sequence + 1) & SEQUENCE_MASK;
            if (sequence == 0) {
                // 序列号耗尽，等待下一毫秒
                currentTimestamp = waitNextMillis(currentTimestamp);
            }
        } else {
            // 新毫秒，序列号重置为0
            sequence = 0L;
        }

        // 3. 记录最新时间戳
        lastTimestamp = currentTimestamp;

        // 4. 组装64位ID
        return (currentTimestamp - START_TIMESTAMP) << 22 |  // 41位时间戳（左移22位）
                DATA_CENTER_ID << 17 |                        // 5位数据中心ID（左移17位）
                WORKER_ID << 12 |                            // 5位工作节点ID（左移12位）
                sequence;                                    // 12位序列号
    }

    // ---------------------- 辅助方法：等待下一毫秒 ----------------------
    private long waitNextMillis(long timestamp) {
        long current = System.currentTimeMillis();
        while (current <= timestamp) {
            current = System.currentTimeMillis();
        }
        return current;
    }

}